﻿using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Threading;
using System.Threading.Tasks;
using Zj.CourseManage.Domain.Events;

namespace Zj.CourseManage.Messaging.RabbitMQ
{
    /// <summary>
    /// Use rabbit message queue to extend event bus
    /// </summary>
    public class RabbitEventBus : IEventBus
    {
        private readonly IMessageSerializer _messageSerializer;
        private readonly IConnectionFactory _connectionFactory;
        private readonly IConnection _connection;
        private readonly IModel _channel;
        private readonly string _exchangeName;
        private readonly string _queueName;
        private readonly string _exchangeType;


        public RabbitEventBus(IMessageSerializer messageSerializer,
            IConnectionFactory connectionFactory,
            IConnection connection,
            IModel channel,
            string exchangeName,
            string queueName,
            string exchangeType)
        {
            this._messageSerializer = messageSerializer;
            this._connectionFactory = connectionFactory;
            this._connection = connection;
            this._channel = channel;
            this._exchangeName = exchangeName;
            this._queueName = queueName;
            this._exchangeType = exchangeType;

            this._channel.ExchangeDeclare(this._exchangeName, this._exchangeType);

            this._queueName = this.InitializeMessageConsumer(queueName);
        }


        public async Task PublishAsync<TEvent>(TEvent @event, CancellationToken cancellationToken = default(CancellationToken)) where TEvent : IEvent
        {
            var messageBody =await _messageSerializer.SerializeAsync<TEvent>(@event);
            _channel.BasicPublish(this._exchangeName,
                @event.GetType().FullName,
                null,
                messageBody);
        }

        public void Subscribe()
        {
            throw new NotImplementedException();
        }

        #region IDisposable Support
        private bool disposedValue = false; // 要检测冗余调用

        protected virtual void Dispose(bool disposing)
        {
            if (!disposedValue)
            {
                if (disposing)
                {
                    // TODO: 释放托管状态(托管对象)。
                    this._channel.Dispose();
                    this._connection.Dispose();
                }

                // TODO: 释放未托管的资源(未托管的对象)并在以下内容中替代终结器。
                // TODO: 将大型字段设置为 null。
                disposedValue = true;
            }
        }

        // TODO: 仅当以上 Dispose(bool disposing) 拥有用于释放未托管资源的代码时才替代终结器。
        // ~RabbitEventBus() {
        //   // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
        //   Dispose(false);
        // }

        // 添加此代码以正确实现可处置模式。
        public void Dispose()
        {
            // 请勿更改此代码。将清理代码放入以上 Dispose(bool disposing) 中。
            Dispose(true);
            // TODO: 如果在以上内容中替代了终结器，则取消注释以下行。
            // GC.SuppressFinalize(this);
        }
        #endregion


        private string InitializeMessageConsumer(string queueName)
        {
            this._channel.QueueDeclare(queueName, true, false, false, null);

            var consumer = new EventingBasicConsumer(this._channel);
            consumer.Received += Consumer_Received;
            this._channel.BasicConsume(queueName, true, consumer);
            return queueName;
        }

        private void Consumer_Received(object sender, BasicDeliverEventArgs e)
        {
            var @event = _messageSerializer.DeSerialize(e.Body);
        }
    }
}
